package com.dream.technology.components.FlowService.imp;

import com.alibaba.fastjson.JSON;
import com.dream.technology.components.FlowService.FlowDealService;
import com.dream.technology.components.FlowService.FlowProcessService;
import com.dream.technology.components.context.FlowContext;
import com.dream.technology.components.context.TagContext;
import com.dream.technology.components.engine.FlowEngine;
import com.dream.technology.components.engine.JsEngineEVel;
import com.dream.technology.components.expresion.imp.RuleExpressionAnalysis;
import com.dream.technology.components.expresion.imp.TagExpressionAnalysis;
import com.dream.technology.components.riskControl.SymbolConstan;
import com.dream.technology.db.entity.*;
import com.dream.technology.db.entity.vo.*;
import com.dream.technology.service.*;
import com.dream.technology.utils.ZYStringUtils;
import com.dream.technology.utils.parse.ObjectUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 描述：流程处理
 *
 * @author chen_q_i@163.com
 * 2019/1/2 : 10:47.
 * @version : 1.0
 */
@Service("flowProcessService")
public class FlowProcessServiceImpl implements FlowProcessService {
    private static final Logger logger = LoggerFactory.getLogger(FlowProcessServiceImpl.class);

    /*@Autowired
    private UserCenterApiClient userCenterApiClient;
    @Autowired
    private CreditInnerClient apiClient;*/

    @Autowired
    private RcFlowService rcFlowService;
    @Autowired
    private RcFlowItemService rcFlowItemService;
    @Autowired
    private RcRuleCollectionService rcRuleCollectionService;

    @Autowired
    private TagContext tagContext;
    @Autowired
    private TagExpressionAnalysis tagExpressionAnalysis;

    @Autowired
    private RuleExpressionAnalysis ruleExpressionAnalysis;

    @Autowired
    private RcFlowLogService flowLogService;
    @Autowired
    private RcFlowParentService parentService;
    @Autowired
    private FlowDealService flowDealService;

    @Autowired
    RcborrowingSnapshotService rcborrowingSnapshotService;


    /**
     * 根据表单构建流程上下文 主要是放入customer borrowingForm
     *
     * @return
     */

    @Override
    public FlowContext buildFlowContext(OrderBorrowingForm orderBorrowingForm) {

        long start = System.currentTimeMillis();
//         获取用户信息 转map
//        GenericRspDTO<CusCustomerVo> customerReq = userCenterApiClient.getCustomerById(orderBorrowingForm.getCustomerId());
//        CusCustomerVo customer = customerReq.getBody();
//        Map<String, Object> customerMap = ObjectUtil.objToMap(customer);
//         订单转map
        Map<String, Object> formMap = ObjectUtil.objToMap(orderBorrowingForm);
        // 初始化基础数据
        FlowContext context = FlowEngine.createFlowContext(new HashMap<>(), formMap);

//        // 授信信息
//        GenericRspDTO<Map<String, Object>> personAllInfo = apiClient.getPersonAllInfo(orderBorrowingForm.getCustomerId());
//        Map<String, Object> stringObjectMap = hand1ePersonAllInfo(personAllInfo);
//        // 生成快照
//        context.putAll(stringObjectMap);
//        RcborrowingSnapshot rcborrowingSnapshot = buildSnapshot(orderBorrowingForm, customer);
//        RcborrowingSnapshot rcborrowingSnapshot1 = buildRcborrowingSnapshot(rcborrowingSnapshot, context);
//        context.setSnapshot(rcborrowingSnapshot1);
//        logger.info("初始化流程上下文 {} 用时{}", JSON.toJSONString(context), System.currentTimeMillis() - start);
        return context;
    }

    RcborrowingSnapshot buildRcborrowingSnapshot(RcborrowingSnapshot rcborrowingSnapshot,FlowContext context){

        rcborrowingSnapshot.setSex(Boolean.valueOf(String.valueOf( context.get("idCard.gender"))) );
        rcborrowingSnapshot.setAge(Integer.valueOf(String.valueOf( context.get("idCard.age"))) );
//        rcborrowingSnapshot.setXiguaScore(Integer.valueOf(String.valueOf( context.get("tianjiReport.xgScore"))) );
//        rcborrowingSnapshot.setZhimaScore(Integer.valueOf(String.valueOf( context.get("zhimaFen.zmScore"))) );
        return rcborrowingSnapshot;
    }


   /* public Map<String, Object> hand1ePersonAllInfo(GenericRspDTO<Map<String, Object>> personAllInfo) {
        Map<String, Object> body = personAllInfo.getBody();
        Map<String, Object> data = new HashMap<>();
        body.entrySet().stream().filter(Objects::nonNull
        ).forEach(entry -> {
            Object value = entry.getValue();
            boolean b = value instanceof JSONObject;
            boolean c = value instanceof JSONArray;
            if (b) {
                value = ((JSONObject) value).getInnerMap();
            } else if (c) {
                value = ((JSONArray) value).toJavaList(Map.class);
            } else {
                value = ObjectUtil.objToMap(value);
            }
            data.put(entry.getKey(), value);
        });

        return data;
    }*/

    /**
     * 还需要拿到用户的授信信息 年龄，地区 ，西瓜分 ，芝麻分
     *
     * @param form
     * @param cus
     * @return
     */
    /*private RcborrowingSnapshot buildSnapshot(OrderBorrowingForm form, CusCustomerVo cus) {

        // 调用授信模块 拿到西瓜分 芝麻分
        RcborrowingSnapshot rcborrowingSnapshot = new RcborrowingSnapshot(form, cus);
        return rcborrowingSnapshot;

    }*/

    /**
     * 借款表单
     */
    @Override
    public String startRun(OrderBorrowingForm borrowingForm) throws Exception {
        Long flowId = 1L;

        FlowContext flowContext = null;
        VoFlowAll voFlowAll = null;
        // 运算规则s
        // 运算规则集
        // 运算完成  生成快照 记录阻断
        // 进入审核
        try {
            // 获取基本信息
            flowContext = buildFlowContext(borrowingForm);
            // 获取流程配置
            RcFlow flow = rcFlowService.getFlowByPlatformApi(borrowingForm.getProductId());
            List<RcFlowItem> rcChildFlows = rcFlowItemService.selectByFlowId(flow.getId());
            // 或取平台统计
            //  解析流程
            //  分解流程 拿到标签配置上开始执行规则流程的某一部分
            VoCustomerTag customerTag = getCustomerTag(flowContext, flow);



            long customerTagId = customerTag.getCustomerTag().getId();
            // 完善快照信息
            RcborrowingSnapshot snapshot = flowContext.getSnapshot();
            snapshot.setRiskControlFlowId(flow.getId());
            snapshot.setCustomerTagId(customerTagId);
            // 已获取用户标签id 拿到对应的标签子项
            RcFlow childFlow = parentService.getChildFlow(rcChildFlows, customerTagId);
            List<RcFlowItem> rcFlowItems = rcFlowItemService.selectByFlowId(childFlow.getId());
            List<RcFlowItem> collect = rcFlowItems.stream().filter(item -> item.getChildItemId() == customerTagId
                    && item.getChildItemType().equals(Integer.valueOf(SymbolConstan.FLOW_ITEM_TYPE_TAG))).collect(Collectors.toList());
            RcFlowItem item = collect.get(0);
            // 拼装voflowall 对象 这里只是流程中一部分
            List<RcFlowItem> ruleAndCollect = getRuleAndCollect(flowContext, childFlow, item, rcFlowItems);
            voFlowAll = getVoFlowAll(childFlow, item, ruleAndCollect, customerTag);
            dealFlow(flowContext, voFlowAll);
        } catch (RuntimeException e) {
            logger.error("风控处理错", e);
            // 异常及记录 保存快照
            flowLogService.insertExcption(flowContext, voFlowAll, e);
//            logger.error(this, e);
            saveSnap(flowContext, false);
            // 取消订单
//            RiskContrlBorrowingResult riskContrlBorrowingResult = new RiskContrlBorrowingResult(borrowingForm.getOrderId(), SymbolConstan.FLOW_RESULT_STOP, e.getMessage());
//            flowDealService.sendResult(JSON.toJSONString(riskContrlBorrowingResult));
        } catch (Exception e) {
            logger.error(" 插入失败日志错误", e);
        }

        return "";
    }


    /**
     * 处理流程
     *
     * @param flowContext
     * @param voFlowAll
     */
    public void dealFlow(FlowContext flowContext, VoFlowAll voFlowAll) throws Exception {

        Map<String, Map<RcFlowItem, Object>> itemMaps = voFlowAll.getItemMaps();
        String flowRule = flowContext.getFlowRule();
        Map<String, String> expMap = FlowEngine.getExpMap(flowRule);
        String newExp = FlowEngine.getNewExp(flowRule);

        for (String rule : newExp.split("&")) {
            //开始处理每个流程子项
            if (StringUtils.isNotBlank(rule) && !rule.contains("arr")) {
                Map<RcFlowItem, Object> itemObjectMap = itemMaps.get(rule);
                if (itemObjectMap == null) {
                    logger.error("流程执行报错，无表达式对应的执行单元 流程{} 表达式 {} 表单{}", voFlowAll, rule, flowContext);
                    throw new RuntimeException("流程执行报错，无表达式对应的执行单元");
                }
                // 处理流程中的每个节点 ，生成记录
                for (Map.Entry<RcFlowItem, Object> entry : itemObjectMap.entrySet()) {
                    RcFlowItem k = entry.getKey();
                    Object v = entry.getValue();
                    if (k.getChildItemType().equals(Integer.valueOf(SymbolConstan.FLOW_ITEM_TYPE_TAG))) {
                        //处理标签
                        handleTag(flowContext, voFlowAll, k, (VoCustomerTag) v);
                    } else if (k.getChildItemType().equals(Integer.valueOf(SymbolConstan.FLOW_ITEM_TYPE_RULE))) {
                        //处理规则
                        boolean result = handleRule(flowContext, voFlowAll, null, (VoRuleAll) v);
                        boolean stratyge = processResult(flowContext, k, result);
                        logFlowItemStratyge(flowContext, voFlowAll, k, v, result, stratyge);
                    } else {
                        // 处理规则集
                        boolean b = handleRuleCollect(flowContext, voFlowAll, (VoRuleCollectionAll) v);
                        boolean stratyge = processResult(flowContext, k, b);
                        logFlowItemStratyge(flowContext, voFlowAll, null, v, b, stratyge);
                    }

                }
            } else {
                // 处理流程中或的关系
                if (StringUtils.isNotBlank(rule)) {
                    String newRule = expMap.get(rule);
                    String[] ruleChar = newRule.split("[|&]");
                    Map<String, Boolean> results = new HashMap<>(32);
                    for (String str : ruleChar) {
                        // 处理流程中的每个节点 ，生成记录
                        for (Map.Entry<RcFlowItem, Object> entry : itemMaps.get(str).entrySet()) {
                            RcFlowItem k = entry.getKey();
                            Object v = entry.getValue();
                            if (k.getChildItemType().equals(Integer.valueOf(SymbolConstan.FLOW_ITEM_TYPE_TAG))) {
                                //处理标签
                                boolean b = k.getAction().equals(SymbolConstan.FLOW_ACTION_STOP);
                                results.put(str, b);
                                handleTag(flowContext, voFlowAll, k, (VoCustomerTag) v);
                            } else if (k.getChildItemType().equals(Integer.valueOf(SymbolConstan.FLOW_ITEM_TYPE_RULE))) {
                                //处理规则
                                boolean result = handleRule(flowContext, voFlowAll, null, (VoRuleAll) v);
                                boolean stratyge = processResult(flowContext, k, result);
                                logFlowItemStratyge(flowContext, voFlowAll, k, v, result, stratyge);
                                results.put(str, stratyge);
                            } else {
                                // 处理规则集
                                boolean b = handleRuleCollect(flowContext, voFlowAll, (VoRuleCollectionAll) v);
                                boolean stratyge = processResult(flowContext, k, b);
                                logFlowItemStratyge(flowContext, voFlowAll, null, v, b, stratyge);
                                results.put(str, stratyge);
                            }
                        }
                    }
                    //拼接流程表达式
                    for (Map.Entry<String, Boolean> entry : results.entrySet()) {
                        String k = entry.getKey();
                        Boolean v = entry.getValue();
                        newRule = newRule.replace(k, String.valueOf(v));
                    }
                    Boolean pass = JsEngineEVel.getBoolean(newRule);
                    flowContext.stopFlow = pass;
                    if (!pass) {
                        flowContext.stopFlow();
                    } else {
                        flowContext.passFlow();
                    }
                }
            }
            if (flowContext.stopFlow) {
                break;
            }
        }
        if (!flowContext.stopFlow) {
            // 快照记录通过
            saveSnap(flowContext, true);
            // 进入审批流程
            RiskContrlBorrowingResult riskContrlBorrowingResult = new RiskContrlBorrowingResult((String) flowContext.get(FlowContext.ORDERFORM_ID), SymbolConstan.FLOW_RESULT_PASS, "通过");
            flowDealService.sendResult(JSON.toJSONString(riskContrlBorrowingResult));
        } else {
            // 快照记录拒绝
            saveSnap(flowContext, false);
            //发送到拒绝队列
            RiskContrlBorrowingResult riskContrlBorrowingResult = new RiskContrlBorrowingResult((String) flowContext.get(FlowContext.ORDERFORM_ID), SymbolConstan.FLOW_RESULT_STOP, "拒绝");
            flowDealService.sendResult(JSON.toJSONString(riskContrlBorrowingResult));
        }
    }

    public void saveSnap(FlowContext flowContext, boolean b) {
        RcborrowingSnapshot snapshot = flowContext.getSnapshot();
        snapshot.setPass(b);
        rcborrowingSnapshotService.insert(snapshot);
    }

    /**
     * 处理结果
     *
     * @param context
     * @param item
     * @param
     */
    public boolean processResult(FlowContext context, RcFlowItem item, boolean result) {
        boolean action = item.getAction().equals(SymbolConstan.RULE_ACTION_PASS);
        boolean b1 = result == action;
        // 如果一样 直接执行，不一样执行相反的策略
        Integer strategy = item.getStrategy();
        boolean b = (b1 && strategy == 0) || (!b1 && strategy == 1);
        if (b) {
            context.stopFlow();
        }
        return b1;

    }

    /**
     * 记录 流程下规则，规则集的处理结果
     * 标签已处理
     *
     * @param context
     * @param item
     * @param v
     * @param result
     */
    public void logFlowItemStratyge(FlowContext context, VoFlowAll voFlowAll, RcFlowItem item, Object v, boolean result, boolean stratyge) throws Exception {
        if (item == null) {
            //规则集处理记录
            flowLogService.insertCollect(context, voFlowAll, (VoRuleCollectionAll) v, result, stratyge);
        } else if (item.getChildItemType().equals(Integer.valueOf(SymbolConstan.FLOW_ITEM_TYPE_RULE))) {
            //处理规则处理记录
            flowLogService.insertRule(context, voFlowAll, null, (VoRuleAll) v, result, stratyge);
        }
    }

    public void handleTag(FlowContext context, VoFlowAll voFlowAll, RcFlowItem item, VoCustomerTag tag) throws Exception {
        boolean b = item.getStrategy().equals(SymbolConstan.FLOW_ACTION_STOP);
        if (b) {
            //标签直接拒绝
            context.stopFlow();
            context.putEnTime();
            //拒绝日志记录日志
            flowLogService.insertTag(context, voFlowAll, tag, b);
        } else {
            //记录通过标签
            flowLogService.insertTag(context, voFlowAll, tag, b);
        }
    }

    /**
     * 解析流程中的一条规则
     * <p>
     * fieldname自包含是否属于第三方信息
     *
     * @param context
     * @param voRuleAll
     */
    public boolean handleRule(FlowContext context, VoFlowAll voFlowAll, VoRuleCollectionAll voRuleCollectionAll, VoRuleAll voRuleAll) throws Exception {
        boolean b = false;
        for (Map.Entry<String, RcRuleItem> entry : voRuleAll.getItems().entrySet()) {
            String s = entry.getKey();
            RcRuleItem ruleItem = entry.getValue();
            String fieldCode = ruleItem.getFieldCode();
            // 属于从第三方获取的字段
            if (fieldCode.contains(SymbolConstan.SEVER_STR)) {
                String serverName = fieldCode.split("\\.")[0];
                // 查看是否已经请求
                // 第一个字母小写
                String serverNameUpper = ZYStringUtils.toLowerCaseFirstOne(serverName);
                boolean contains = context.getServiceNames().contains(serverNameUpper);
                // 未请求
                if (!contains) {
                    //请求三方数据
                    prepareThirdParty(context, serverName);
                }
            }
            // 拼接表达式计算
            b = ruleExpressionAnalysis.analysisRule(voRuleAll, context);
            // 流程结果记录在流程记录中
            if (voRuleCollectionAll != null) {
                if (b) {
                    // 通过规则记录
                    flowLogService.insertRule(context, voFlowAll, voRuleCollectionAll, voRuleAll, b, true);
                } else {
                    //规则拒绝 记录拒绝
                    flowLogService.insertRule(context, voFlowAll, voRuleCollectionAll, voRuleAll, b, false);
                }
            }
        }
        return b;
    }

    /**
     * 解析流程中的一条规则集
     * <p>
     * fieldname自包含是否属于第三方信息
     *
     * @param context
     * @param voRuleCollectionAll
     */
    public boolean handleRuleCollect(FlowContext context, VoFlowAll voFlowAll, VoRuleCollectionAll voRuleCollectionAll) throws Exception {
        boolean b = false;
        Map<String, Boolean> booleanMap = new HashMap<>();
        String collectionRule = voRuleCollectionAll.getRcRuleCollection().getCollectionRule();

        String[] split = collectionRule.split("[()&|]");
        List<String> strings = Arrays.stream(split).filter(str -> !str.isEmpty()).collect(Collectors.toList());
        voRuleCollectionAll.getItems().forEach((k, v) -> {
            //表达式有才跑
            if (strings.contains(k)) {
                try {
                    booleanMap.put(k, handleRule(context, voFlowAll, voRuleCollectionAll, v));
                } catch (Exception e) {
                    logger.error("规则集处理错误", e);
                }
            }
        });
        // 大坑 需要先替换长的，后替换短的
        strings.sort((x, y) -> {
            if (x.length() > y.length()) {
                return -1;
            }
            return 1;
        });
//        for (Map.Entry<String, Boolean> entry : booleanMap.entrySet()) {
//            String k = entry.getKey();
//            Boolean v = entry.getValue();
//            collectionRule = collectionRule.replace(k, String.valueOf(v));
//        }
        for (int i = 0; i <strings.size() ; i++) {
            String key = strings.get(i);
            Boolean aBoolean = booleanMap.get(key);
            collectionRule = collectionRule.replace(key, String.valueOf(aBoolean));
        }
        b = JsEngineEVel.getBoolean(collectionRule);

        return b;
    }


    /**
     * 获取三方数据
     *
     * @param context
     * @param serverName
     */
    @Override
    public void prepareThirdParty(FlowContext context, String serverName) {
//        long start = System.currentTimeMillis();
//        logger.info("开始调用第三方服务 {}", serverName);
//        BaseCollarService bean = (BaseCollarService) SpringUtil.getBean(serverName);
//        try {
//            bean.execute(context);
//        } catch (Exception e) {
//            logger.error("请求三方服务出错 服务名称 ：{} 流程 ：{}", serverName, context);
//            logger.error("调用三方出错", e);
//            //出错 终止流程
//            throw new RuntimeException("调用三方出错", e);
//        }
//        logger.info("结束调用第三方服务 {} ,useTime :{}", serverName, System.currentTimeMillis() - start);
    }

    /**
     * 组装映射规则流程下所有的子项
     *
     * @param
     * @return
     */
    public VoFlowAll getVoFlowAll(RcFlow flow, RcFlowItem item, List<RcFlowItem> ruleAndCollect, VoCustomerTag customerTag) {
        Map<String, Map<RcFlowItem, Object>> voRuleCollAllMap = new HashMap<String, Map<RcFlowItem, Object>>() {
            {
                put(item.getItemAlias(), new HashMap<RcFlowItem, Object>() {
                    {
                        put(item, customerTag);
                    }
                });
            }
        };
        if (!ruleAndCollect.isEmpty()) {
            voRuleCollAllMap.putAll(rcRuleCollectionService.getVoRuleCollAll(ruleAndCollect));
        }
//        voRuleCollAllMap.put(item.getItemAlias(), new HashMap<RcFlowItem, Object>() {
//            {
//                put(item, customerTag);
//            }
//        });
        VoFlowAll voFlowAll = new VoFlowAll();
        voFlowAll.setFlow(flow);
        voFlowAll.setItemMaps(voRuleCollAllMap);
        return voFlowAll;
    }


    /**
     * 拿到标签后分解 流程配置的规则 规则集
     *
     * @param context
     * @param flow
     * @param item
     * @return
     */
    public List<RcFlowItem> getRuleAndCollect(FlowContext context, RcFlow flow, RcFlowItem item, List<RcFlowItem> items) {
        // 查询这一部风的配置项，组成新的对象
        // 由ruleItem 组成
        String finalFlowRule = flow.getFlowRule();
        context.setFlowRule(finalFlowRule);
        List<RcFlowItem> collect = items.stream().filter(flowItem -> !flowItem.getChildItemType()
                .equals(Integer.valueOf(SymbolConstan.FLOW_ITEM_TYPE_TAG)))
                .collect(Collectors.toList());
        return collect;
    }

    /**
     * 给用户打标签
     * 获取数据
     * 执行标签的一条规则
     * 直到某个标签全部返回true
     * 如果到最后没有命中标签 配置错误，结束流程
     *
     * @param context
     * @param flow
     * @return
     */
    @Override
    public VoCustomerTag getCustomerTag(FlowContext context, RcFlow flow) {
        Boolean isHit = false;
        VoCustomerTag tagTrue = null;
        // 获取流程中标签配置
        List<VoCustomerTag> voTags = tagContext.getVoTag(flow.getId());
        // 遍历标签
        for (VoCustomerTag tag : voTags) {
            isHit = tagExpressionAnalysis.analysisTag(tag, context);
            if (isHit) {
                tagTrue = tag;
                break;
            }
        }
        // 都不匹配报错
        if (!isHit) {
            context.stopFlow();
            logger.error("标签无法覆盖该用户 form ：{} , tag :{}", context, voTags);
            throw new RuntimeException("标签无法覆盖该用户" +flow.getFlowName());
        }
        return tagTrue;
    }


}
